<!DOCTYPE html>
<meta charset=utf-8>
<title>Animation.progress</title>
<link rel="help"
href="https://drafts.csswg.org/web-animations-2/#the-progress-of-an-animation">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../../testcommon.js"></script>
<script src="../../../scroll-animations/scroll-timelines/testcommon.js"></script>
<body>
  <style>
    .scroller {
      overflow-x: hidden;
      overflow-y: auto;
      height: 200px;
      width: 100px;
      will-change: transform;
    }
    .contents {
      height: 1200px;
      width: 100%;
    }
    #target {
      height:  100px;
      width:  100px;
      background-color: green;
      margin-top: -1000px;
    }
  </style>
<div id="log"></div>
<script>
'use strict';

promise_test(async t => {
  const animation = createScrollLinkedAnimation(t);
  const scroller = animation.timeline.source;
  const maxScroll = scroller.scrollHeight - scroller.clientHeight;

  assert_equals(animation.currentTime, null,
      "The current time is null in Idle state.");
  assert_equals(animation.progress, null,
      "The progress is null since the currentTime is unresolved.");

  animation.play();
  assert_true(animation.pending, "Animation is in the pending state.");
  assert_equals(animation.currentTime, null,
      "The current time remains null while in the pending state.");
  assert_equals(animation.progress, null,
      "The progress is null since the currentTime is unresolved.");

  await animation.ready;
  // Verify initial start and current times once ready.
  assert_percents_equal(animation.currentTime, 0,
      "The current time is resolved when ready.");
  assert_equals(animation.progress, 0, "The progress is should be zero.");

  scroller.scrollTop = 0.4 * maxScroll;

  await waitForNextFrame();
  assert_percents_equal(animation.currentTime, 40,
      "currentTime reflects progress as a percentage");
  assert_approx_equals(animation.progress, 0.4, 0.01,
      "The progress is should match the scroll progress.");
}, "animation.progress reflects the progress of a scroll animation as a "+
   "number between 0 and 1");

promise_test(async t => {
  const animation = createScrollLinkedAnimation(t);
  const scroller = animation.timeline.source;
  const maxScroll = scroller.scrollHeight - scroller.clientHeight;

  animation.play();
  await animation.ready;
  // Verify initial start and current times once ready.
  assert_percents_equal(animation.currentTime, 0,
      "The current time is resolved when ready.");
  assert_equals(animation.progress, 0, "The progress is should be zero.");

  scroller.scrollTop = 0.4 * maxScroll;
  await waitForNextFrame();

  let timing = animation.effect.getComputedTiming();
  // iteration duration should be 100%, progress should reflect 40%.
  assert_percents_equal(timing.duration, 100);
  assert_percents_equal(animation.currentTime, 40,
      "currentTime reflects progress as a percentage");
  assert_approx_equals(animation.progress, 0.4, 0.01,
      "The progress is should match the scroll progress.");
      timing = animation.effect.getComputedTiming();

  animation.effect.updateTiming({ iterations: 2 });

  timing = animation.effect.getComputedTiming();
  // iteration duration should be 50%, progress should still reflect 40%
  // as it measures currentTime / effect endTime.
  assert_percents_equal(timing.duration, 50);
  assert_percents_equal(animation.currentTime, 40,
      "currentTime reflects progress as a percentage");
  assert_approx_equals(animation.progress, 0.4, 0.01,
      "The progress is should match the scroll progress.");
}, "animation.progress reflects the overall progress of a scroll animation " +
   "with multiple iterations.");

promise_test(async t => {
  const scroller = createScroller(t);
  const view_timeline = createViewTimeline(t);

  const animation = createAnimation(t);
  animation.rangeStart = { rangeName: 'contain', offset: CSS.percent(10) };
  animation.rangeEnd = { rangeName: 'contain', offset: CSS.percent(90) };
  animation.timeline = view_timeline;

  await animation.ready;

  // Cover range is [0, 300]
  // Contain range is [100, 200]
  // rangeStart offset of 10% => 110px
  // rangeEnd offset of 90% => 190px
  const target_pct = 40;
  const target_scroll_top = 110 + (target_pct / 100) * (190 - 110);
  scroller.scrollTop = target_scroll_top;
  await waitForNextFrame();

  const start_time = 110 / 300 * 100;
  const timeline_time = target_scroll_top / 300 * 100;
  const expected_current_time = timeline_time - start_time;
  assert_percents_equal(animation.currentTime, expected_current_time,
      "currentTime reflects progress as a percentage");
  assert_approx_equals(animation.progress, target_pct / 100, 0.01,
    "progress should reflect fraction of view timeline range scroll through.");
}, "animation.progress reflects the overall progress of a scroll animation " +
   "that uses a view-timeline.");

promise_test(async t => {
  const scroller = createScroller(t);
  const view_timeline = createViewTimeline(t);

  const animation = createAnimation(t);
  animation.rangeStart = { rangeName: 'contain', offset: CSS.percent(10) };
  animation.rangeEnd = { rangeName: 'contain', offset: CSS.percent(90) };
  animation.timeline = view_timeline;

  await animation.ready;

  // Cover range is [0, 300]
  // Contain range is [100, 200]
  // rangeStart offset of 10% => 110px
  // rangeEnd offset of 10% => 190px

  scroller.scrollTop = 100;
  await waitForNextFrame();
  let timing = animation.effect.getComputedTiming();
  assert_less_than(animation.currentTime.value, 0, "currentTime is negative");
  assert_approx_equals(animation.progress, 0, 0.01, "progress is zero when " +
    "scroll offset is less than range start.");

  scroller.scrollTop = 200;
  await waitForNextFrame();
  assert_approx_equals(animation.currentTime.value, timing.endTime.value, 0.01,
    "currentTime has reached endTime");
  assert_approx_equals(animation.progress, 1, 0.01, "progress is one when " +
    "scroll offset goes past than range end.");
}, "progresss of a view-timeline is bounded between 0 and 1.");

</script>
</body>
